﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using System.Collections.Generic;

namespace Tesla.Graphics {
    internal struct RenderStateRecord {
        public DepthStencilState DepthStencilState;
        public RasterizerState RasterizerState;
        public BlendState BlendState;
    }

    internal class RenderStateMap {
        private Dictionary<String, RenderStateRecord[]> _map;
        private String _activeTechniqueName;
        private RenderStateRecord[] _activeRecords;

        public RenderStateMap() {
            _map = new Dictionary<String, RenderStateRecord[]>();
            _activeRecords = null;
        }

        public void SetRenderState(IEffectTechnique technique, int passIndex, RenderState rs) {
            RenderStateRecord[] records;
            if(!_map.TryGetValue(technique.Name, out records)) {
                records = new RenderStateRecord[technique.Passes.Count];
                _map.Add(technique.Name, records);
            }

            if(passIndex >= 0 && passIndex < records.Length) {
                RenderStateRecord record = records[passIndex];
                switch(rs.StateType) {
                    case RenderStateType.BlendState:
                        record.BlendState = rs as BlendState;
                        break;
                    case RenderStateType.DepthStencilState:
                        record.DepthStencilState = rs as DepthStencilState;
                        break;
                    case RenderStateType.RasterizerState:
                        record.RasterizerState = rs as RasterizerState;
                        break;
                }
                records[passIndex] = record;
            } else {
                throw new ArgumentOutOfRangeException("Pass index out of range");
            }
        }

        public RenderState GetRenderState(RenderStateType type, String techName, int passIndex) {
            RenderStateRecord[] records;
            if(!_map.TryGetValue(techName, out records)) {
                return null;
            }

            if(passIndex >= 0 && passIndex < records.Length) {
                RenderStateRecord record = records[passIndex];
                switch(type) {
                    case RenderStateType.BlendState:
                        return record.BlendState;
                    case RenderStateType.DepthStencilState:
                        return record.DepthStencilState;
                    case RenderStateType.RasterizerState:
                        return record.RasterizerState;
                    default:
                        return null;
                }
            } else {
                throw new ArgumentOutOfRangeException("Pass index out of range");
            }
        }

        public void Clear() {
            _map.Clear();
        }

        public void SetActiveRecords(IEffectTechnique technique) {
            _activeTechniqueName = technique.Name;
            _map.TryGetValue(_activeTechniqueName, out _activeRecords);
            if(_activeRecords == null) {
                _activeRecords = new RenderStateRecord[technique.Passes.Count];
                _map.Add(technique.Name, _activeRecords);
            }
        }

        public void ApplyRenderStates(IRenderer renderer, int passIndex) {
            if(_activeRecords != null) {
                if(passIndex >= 0 && passIndex < _activeRecords.Length) {
                    RenderStateRecord record = _activeRecords[passIndex];
                    renderer.ApplyRenderState(RenderStateType.BlendState, record.BlendState);
                    renderer.ApplyRenderState(RenderStateType.DepthStencilState, record.DepthStencilState);
                    renderer.ApplyRenderState(RenderStateType.RasterizerState, record.RasterizerState);
                }
            } // fail silently
        }

        public RenderStateMap Clone() {
            RenderStateMap clone = new RenderStateMap();
            foreach(KeyValuePair<String, RenderStateRecord[]> kv in _map) {
                clone._map.Add(kv.Key, (RenderStateRecord[]) kv.Value.Clone());
            }
            clone._activeRecords = clone._map[_activeTechniqueName];
            clone._activeTechniqueName = _activeTechniqueName;
            return clone;
        }
    }
}
